/******************************************************************************
* 文件名称： bsp_Key.c
* 模块名称： 通用按键模块
* 版 本 号： V1.0
* 作    者： PLAY
* 备    注： 通过状态机实现。按键扫描需定时执行，一般采用20ms。如果按键扫描放在中断中，
*            需配合FIFO使用。否则会丢失按键值导致按键不灵敏
* 修改记录： 版本     修改人      时间         修改内容
*		    V1.0	  PLAY	   2021.09.02
*****************************************************************************/

/******************************************************************************
 头文件
*****************************************************************************/
#include "bsp_key.h"
#include "Config.h"
#include "App_Protocol.h"
/******************************************************************************
 本文件宏定义 ('#define')
*****************************************************************************/

/*定义按键状态*/
#define KEY_STATE_INIT            	0
#define KEY_STATE_WOBBLE          	1
#define KEY_STATE_PRESS           	2
#define KEY_STATE_LONG            	3
#define KEY_STATE_CONTINUE        	4
#define KEY_STATE_RELEASE         	5
#define KEY_STATE_CONTINUE_RELEASE	6

/******************************************************************************
 本文件类型定义 ('typedef')
*****************************************************************************/

#define uchar unsigned char
#define uint  unsigned short

/******************************************************************************
 本文件静态函数声明 ('static')
*****************************************************************************/

static uchar Key_GetIO(void);

static void vBsp_Key_K5_Init(void);


/******************************************************************************
 全局变量定义
*****************************************************************************/

uchar	s_u8KeyState = KEY_STATE_INIT ;
uint	s_u8KeyTimeCount = 0 ;


/*****************************************************************************
 函数实现 - global ('extern') and local ('static')
*****************************************************************************/


/******************************************************************************
* 功  能：按键初始化函数，主要完成按键FIFO的初始化
* 参  数：无
* 返回值：无
* 备  注：按键相关IO端口的初始，不在此函数中。需要在调用前初始化IO
*****************************************************************************/
void vBsp_Key_Init(void)
{
    muilt_cyc_fifo_error_code_t    lxState;
    MCF_ApplyFifo(KEY_FIFO_LENGTH,KEY_FIFO_ID,&lxState);     /*初始化按键队列*/
#if (KEY_MODE == K5 || (KEY_MODE == K6))
    vBsp_Key_K5_Init();
#endif
}

/******************************************************************************
* 功  能：按键延时用函数
* 参  数：无
* 返回值：K5按键专用，用于改变扫描线电平后的延时
*****************************************************************************/
static void Key_Delay(volatile uint32_t Delay)
{
    while(Delay > 0)
    {
        Delay--;
    }
}

/******************************************************************************
* 功  能：K5按键，扫描线重新初始化函数
* 参  数：无
* 返回值：K5按键专用，用于改变扫描线口线为输出模式
*****************************************************************************/
static void vBsp_Key_K5_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOC_CLK_ENABLE();

    GPIO_InitStruct.Pin   = KEY_MODE_Pin;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(KEY_MODE_GPIO_Port, &GPIO_InitStruct);
}
#if (KEY_MODE == K4)
/******************************************************************************
* 功  能：读取按键相关的IO引脚状态，并根据IO状态返回对应键值
* 参  数：无
* 返回值：对应按键的键值
* 备  注：除K5按键外，其他按键通用
*****************************************************************************/
static uchar Key_GetIO(void)
{
    uchar keyvalue = 0;
    static uint8_t lucPowerKeyRelease = 0;/*标记Power键是否释放过*/

    /*读取6个IO口电平状态，没用到的端口，默认返回无效电平*/
    if(Read_key_1 == KEY_LEVEL)
    {
        keyvalue |= 0x01;
    }
    if(Read_key_2 == KEY_LEVEL)
    {
        keyvalue |= (0x01 << 1);
    }
    if(Read_key_3 == KEY_LEVEL)
    {
        keyvalue |= (0x01 << 2);
    }
    if(Read_key_4 == KEY_LEVEL)
    {
        keyvalue |= (0x01 << 3);
    }
    else
    {
        lucPowerKeyRelease = 1;/*标记Power键已经释放过*/
    }



    /*在Power键未释放之前，不返回任何键值。防止Power键一直按着不松开后直接关机*/
    if(lucPowerKeyRelease)
    {
        return keyvalue;
    }
    else
    {
        return 0;
    }
}

#elif(KEY_MODE == K5)



/******************************************************************************
* 功  能：读取按键相关的IO引脚状态，并根据IO状态返回对应键值
* 参  数：无
* 返回值：K5按键专用
*****************************************************************************/
static uchar Key_GetIO(void)
{
    uint8_t keyvalue = 0;
    uint8_t Key_Up_Temp_0,Key_Up_Temp_1;
    uint8_t Key_Down_Temp_0,Key_Down_Temp_1;
    static uint8_t lucPowerKeyRelease = 0;/*标记Power键是否释放过*/

    /*读取扫描线拉高时Up线和Down线的状态（上次扫描结束时，扫描线就已经被拉高了*/
    Key_Up_Temp_1 	= Read_key_1;
    Key_Down_Temp_1 = Read_key_3;

    Set_key_i_0;		/*再将扫描线拉低,读取此时Up线和Down线的电平状态*/
    Key_Delay(600);	/*电平变化后，加稍许延时，防止引脚电平变化不及时导致读取错误电平*/
    Key_Up_Temp_0	= Read_key_1;
    Key_Down_Temp_0 = Read_key_3;

    /*在任何状态下，UP线都为低电平，说明UP键被按下*/
    if(Key_Up_Temp_0 == KEY_LEVEL && Key_Up_Temp_1 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_1;
    }

    /*在任何状态下，Down线都为低电平，说明Down键被按下*/
    if(Key_Down_Temp_0 == KEY_LEVEL && Key_Down_Temp_1 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_3;
    }

    /*当扫描线为1，UP线为1，扫描线为0，UP线为0，认为i键按下*/
    if(Key_Up_Temp_1 == 1 && Key_Up_Temp_0 == 0)
    {
        keyvalue |= KEY_VALUE_2;
    }

    /*当扫描线为1，Down线为1，扫描线为0，Down线为0，认为大灯键按下*/
    if(Key_Down_Temp_1 == 1 && Key_Down_Temp_0 == 0)
    {
        keyvalue |= KEY_VALUE_5;
    }


    if(Read_key_4 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_4;
    }
    else
    {
        lucPowerKeyRelease = 1;/*标记Power键已经释放过*/
    }

    Set_key_i_1;		/*扫描线拉高，为下一次读取状态做准备*/

    /*在Power键未释放之前，不返回任何键值。防止Power键一直按着不松开后直接关机*/
    if(lucPowerKeyRelease)
    {
        return keyvalue;
    }
    else
    {
        return 0;
    }
}

#elif(KEY_MODE == K6)
/******************************************************************************
* 功  能：读取按键相关的IO引脚状态，并根据IO状态返回对应键值
* 参  数：无
* 返回值：K6按键专用
*****************************************************************************/
static uchar Key_GetIO(void)
{
    uint8_t keyvalue = 0;
    uint8_t Key_Up_Temp_0,Key_Up_Temp_1;
    uint8_t Key_Down_Temp_0,Key_Down_Temp_1;
    uint8_t Key_PWR_Temp_0,Key_PWR_Temp_1;
    static uint8_t lucPowerKeyRelease = 0;/*标记Power键是否释放过*/

    /*读取扫描线拉高时Up线和Down线的状态（上次扫描结束时，扫描线就已经被拉高了*/
    Key_Up_Temp_1 	= Read_key_1;
    Key_Down_Temp_1 = Read_key_3;
    Key_PWR_Temp_1  = Read_key_4;

    Set_key_i_1;		/*再将扫描线拉低,读取此时Up线和Down线的电平状态*/
    Key_Delay(600);	/*电平变化后，加稍许延时，防止引脚电平变化不及时导致读取错误电平*/
    Key_Up_Temp_0	= Read_key_1;
    Key_Down_Temp_0 = Read_key_3;
    Key_PWR_Temp_0 = Read_key_4;

    /*在任何状态下，UP线都为低电平，说明UP键被按下*/
    if(Key_Up_Temp_0 == KEY_LEVEL && Key_Up_Temp_1 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_1;
    }

    /*在任何状态下，Down线都为低电平，说明Down键被按下*/
    if(Key_Down_Temp_0 == KEY_LEVEL && Key_Down_Temp_1 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_3;
    }

    /*当扫描线为1，UP线为1，扫描线为0，UP线为0，认为L键按下*/
    if(Key_Up_Temp_1 == 1 && Key_Up_Temp_0 == 0)
    {
        keyvalue |= KEY_VALUE_7;
    }

    /*当扫描线为1，Down线为1，扫描线为0，Down线为0，认为R键按下*/
    if(Key_Down_Temp_1 == 1 && Key_Down_Temp_0 == 0)
    {
        keyvalue |= KEY_VALUE_6;
    }

    /*在任何状态下，pwr线都为低电平，说明pwr键被按下*/
    if(Key_PWR_Temp_0 == KEY_LEVEL && Key_PWR_Temp_1 == KEY_LEVEL)
    {
        keyvalue |= KEY_VALUE_4;
    }
    else
    {
        lucPowerKeyRelease = 1;/*标记Power键已经释放过*/
    }

    /*当扫描线为1，pwr线为1，扫描线为0，pwr线为0，认为i键按下*/
    if(Key_PWR_Temp_1 == 1 && Key_PWR_Temp_0 == 0)
    {
        keyvalue |= KEY_VALUE_2;
    }

    Set_key_i_0;		/*扫描线拉高，为下一次读取状态做准备*/

    /*在Power键未释放之前，不返回任何键值。防止Power键一直按着不松开后直接关机*/
    if(lucPowerKeyRelease)
    {
        return keyvalue;
    }
    else
    {
        return 0;
    }
}

#endif

/******************************************************************************
* 功  能：按键扫描函数，该函数需定时调用，获取对应的按键状态并存入FIFO
* 参  数：无
* 返回值：无
*****************************************************************************/
uint16_t usKey_Scan(void)
{
    static uint16_t	s_u16LastKey = KEY_NULL ;  		/*保存最初按下时的键值*/

    static uint8_t WobbleCnt = 0;

    uint16_t KeyTemp = KEY_NULL ;

    KeyTemp = Key_GetIO();        	/*获取键值*/

    switch(s_u8KeyState)
    {
    case KEY_STATE_INIT : 		/*初始状态*/
    {
        if(KEY_NULL != (KeyTemp)) 	/*键值不为0*/
        {
            s_u16LastKey = KeyTemp;				/*保存上次的键值*/
            KeyTemp = KEY_NULL;
            s_u8KeyState = KEY_STATE_WOBBLE ; 	/*进入消抖态*/
            WobbleCnt = 0;
        }
        s_u8KeyTimeCount = 0 ;
        break ;
    }

    case KEY_STATE_WOBBLE :      /*消抖*/
    {
        if(WobbleCnt < KEY_WOBBLE_PERIOD / KEY_SCAN_TIME)
        {
            WobbleCnt++;
        }
        else
        {
            s_u8KeyState = KEY_STATE_PRESS ;
        }
        KeyTemp = KEY_NULL;
        break ;
    }

    case KEY_STATE_PRESS :
    {
        if((KEY_NULL != (KeyTemp)) && (s_u16LastKey == KeyTemp))
        {
            s_u16LastKey = KeyTemp ; 	/*保存键值,以便在释放按键状态返回键值*/
            KeyTemp |= KEY_PRESS;  		/*注释掉这句，要求不响应按键按下*/
            //KeyTemp = KEY_NULL;
            s_u8KeyState = KEY_STATE_LONG ;
        }
        else
        {
            KeyTemp = KEY_NULL;
            s_u8KeyState = KEY_STATE_INIT ;
        }
        break ;
    }

    case KEY_STATE_LONG :
    {
        if(KEY_NULL != (KeyTemp))
        {
            if(KeyTemp == s_u16LastKey)
            {
                if(++s_u8KeyTimeCount > KEY_LONG_PERIOD / KEY_SCAN_TIME - 1)
                {
                    s_u8KeyTimeCount = 0 ;
                    KeyTemp |= KEY_LONG;  /*长按键事件发生*/

                    s_u8KeyState = KEY_STATE_CONTINUE ;
                }
                else
                {
                    KeyTemp = KEY_NULL;
                }
            }
            else
            {
                /*这一次的键值和上一次的键值不一致，说明有其它按键按下，返回初始状态*/
                KeyTemp = KEY_NULL;
                s_u8KeyState = KEY_STATE_INIT;
            }
        }
        else
        {
            KeyTemp = KEY_NULL;
            s_u8KeyState = KEY_STATE_RELEASE  ;
        }
        break ;
    }

    case KEY_STATE_CONTINUE :
    {
        if(KEY_NULL != (KeyTemp)) /*连发状态下，只有读取到键值为空，才返回初始状态*/
        {
            if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD / KEY_SCAN_TIME - 1)
            {
                s_u8KeyTimeCount = 0 ;
                //KeyTemp |= KEY_CONTINUE; 	/*注释掉这句，要求不响应连发状态*/
                KeyTemp = KEY_NULL;
            }
            else
            {
                KeyTemp = KEY_NULL;
            }
        }
        else
        {
            KeyTemp = KEY_NULL;
            //s_u8KeyState = KEY_STATE_INIT  ;
            s_u8KeyState = KEY_STATE_CONTINUE_RELEASE;
        }
        break ;
    }
    case KEY_STATE_CONTINUE_RELEASE:
    {
        KeyTemp = s_u16LastKey | KEY_CONTINUE_RELEASE;
        s_u8KeyState = KEY_STATE_INIT;
        break;
    }
    case KEY_STATE_RELEASE :
    {
        s_u16LastKey |= KEY_RELEASE;
        KeyTemp = s_u16LastKey ;
        s_u8KeyState = KEY_STATE_INIT ;
        break ;
    }

    default :
        break ;
    }

    return KeyTemp;
}







